#ifndef ODN_GAMEMGR_H
#define ODN_GAMEMGR_H

#include <opal\opal.h>
#include <ogre\Ogre.h>
#include <ogre\OgreKeyEvent.h>
#include <ogre\OgreEventListeners.h>

#include "ODN_DLLAccess.h"
#include "ODN_Networking.h"
//#include "ODN_GameState.h"
#include "ODN_GameTimer.h"
#include "ODN_InputDevice.h"
#include "ODN_InputBindingMgr.h"
#include "ODN_Singleton.h"
#include "ODN_Containers.h"
#include "ODN_GameWorld.h"
#include "ODN_Maths.h"
#include "ODN_ModelEntity.h"
#include "ODN_ModelBit.h"

//TODO: Look into network interface creation and pointers and think about server
// acting as client and hence only ever one network interface

namespace ouden {

class CGameState;

//This class basically manages the game states and directs basic frame and input
// events to the current game state
class ODN_DLLACCESS CGameMgr : public Ogre::FrameListener, public IInputReceiver, public CSingleton<CGameMgr>
{
	public:
		CGameMgr();
		virtual ~CGameMgr();
		
		//Main function to begin the game
		void start(CGameState* pGameState);
		
		
		//static CGameMgr* getInstance() 		{ return m_pInstance; }
		
		
		//Game state management functions
		void changeState(CGameState* state);
		void pushState(CGameState* state);
		void popState();
		
		//Game management object access functions
		Ogre::Root* 		getOgreRoot() 				{ return m_pRoot; }
		Ogre::RenderWindow* getOgreWindow() 			{ return m_pWindow; }
		IInputDevice* 		getInputDevice() 			{ return m_pInputDevice; }
		CInputBindingMgr* 	getInputBindingMgr() 		{ return m_pInputBindingMgr; }
		Ogre::SceneManager* getSceneMgr()				{ return m_pSceneMgr; }
		Ogre::Camera* 		getMainCamera() 			{ return m_pCamera; }
		CGameWorld* 		getGameWorld() 				{ return m_pGameWorld; }
		CGameTimer* 		getGameTimer() 				{ return m_pGameTimer; }
		opal::Simulator* 	getSimulator() 				{ return m_pSimulator; }
		RakClientInterface* getClientInterface() 		{ return m_pClient->getNetworkInterface(); }
		RakServerInterface* getServerInterface() 		{ return m_pServer->getNetworkInterface(); }
		
		//Network management functions
		bool isServer() 		{ return (m_pServer); }
		bool isClient() 		{ return (m_pClient); }
		void beginServerConnection(MultiplayerServer* pPacketHandler, unsigned short iMaxPlayers, unsigned short iServerPort);
		void beginClientConnection(MultiplayerClient* pPacketHandler, String sHostAddress, unsigned short iServerPort, unsigned short iClientPort);
		
		//IInputReceiver functions forward to current game state
		virtual void onUnBufInput(IInputDevice* pInput);
		virtual void onKeyDown(KeyCode iKey);
		virtual void onKeyUp(KeyCode iKey);
		virtual void onKeyPress(KeyCode iKey);
		virtual void onMouseMove(Vector vPos, MouseButton iButtonState);
		virtual void onMouseButtonUp(Vector vPos, MouseButton iButton);
		virtual void onMouseButtonDown(Vector vPos, MouseButton iButton);
		virtual void onMouseButtonPress(Vector vPos, MouseButton iButton);
		
	protected:
		
		//Game events
		//TODO: Do these need to be virtual?
		//TODO: Maybe wrap these two functions to be onFrameStart and also think about onTick
		
		//Ogre FrameListener events call onTick, frameStarted and frameEnded in current game state
		virtual bool frameStarted(const Ogre::FrameEvent &evt);
		virtual bool frameEnded(const Ogre::FrameEvent &evt);
		
		//Game management objects
		Ogre::Root*				m_pRoot; 			//Root Ogre object provides access to Ogre engine
		Ogre::RenderWindow* 	m_pWindow;			//Main renderer window
		Ogre::EventProcessor* 	m_pEventProcessor; 	//Ogre event processor for setting up input
		Ogre::SceneManager* 	m_pSceneMgr;		//Ogre Scene manager object
		Ogre::Viewport* 		m_pViewport;
		Ogre::Camera* 			m_pCamera;			//Main game camera
		
		IInputDevice* 			m_pInputDevice; 	//Unbuffered input reader
		CInputBindingMgr* 		m_pInputBindingMgr; //Input binding manager
		CGameTimer* 			m_pGameTimer; 		//Main game timer which calls tick and frame events
		CGameWorld* 			m_pGameWorld;		//Game world object which holds all game entities
		
		opal::Simulator* 		m_pSimulator;		//Physics simulator
		
		//TODO: Remove these
		//RakClientInterface* 	m_pNetworkClient; 	//Network client instance
		//RakServerInterface* 	m_pNetworkServer; 	//Network server instance
		//INetworkMgr* 			m_pNetworkMgr;
		
		//Network objects
		//NOTE: At least one of these should always be 0
		CClientInterface* 		m_pClient; 			//Network client handler instance
		CServerInterface* 		m_pServer; 			//Network server handler instance
		
		//Game management flags
		//bool 					m_bIsServer; 		//Is the game acting as a server
		//bool 					m_bIsClient; 		//Is the game acting as a client
		bool m_bFinished; 		//If true then exits main loop on next frame
		
		
		
	private:
		
		//Main engine initialisation function called by run()
		bool init();
		
		//Main engine cleanup function called after the main loop is finished
		bool cleanup();
		
		//Loads all game resources as specified in the resources.cfg file
		bool loadResources();
		
		//Stack of current game states
		Array<CGameState*> m_pGameStates;
	
};

} //namespace ouden

#endif // ODN_GAMEMGR_H
